package Generic;


import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

public abstract class EvolutionaryAlgorithm {
	
	protected Population adults; 
	protected Population children;
	
	protected int populationSize;
	protected double crossoverRate;
	protected double mutationRate;
	protected double generationNumber;
	
	protected static final int DEFAULT_POPULATION_SIZE = 150;
	protected static final double DEFAULT_CROSSOVER_RATE = 1;
	protected static final double DEFAULT_MUTATION_RATE = 0.4;
	public static final int DEFAULT_NUMBER_OF_GENERATIONS = 150;
	
	protected static List<String> additionalParameters;
	protected static List<Double> additionalParameterValues;
	protected static List<String> additionalStatisticNames;
	
	protected abstract void initialize(int popSize,double crossoverRate,double mutationRate);
	
	protected void initializeEvolutionaryAlgorithm(int populationSize,double crossoverRate,double mutationRate)
	{
		this.crossoverRate  = crossoverRate;
		this.populationSize = populationSize;
		this.mutationRate   = mutationRate;
		
		adults = null;
		children = Population.getFirstPopulation();
		Individual individual;
		Genotype gtype;
		
		for(int i = 0; i<populationSize;i++)
		{
			individual = new Individual();
			gtype = generateRandomGenotype();
			individual.setGenotype(gtype);
			children.addIndividual(individual);
		}
		
		develop();
		evaluate();
		
		adults = children;
		children = null;
	}
	
	public void iterateGeneration(){
		performParentSelection();
		
		crossover();
		mutation();
		
		develop();
		evaluate();
		
		performReplacement();
		adults=children;
		children=null;
		
		adults.incrementAge();
		
		children = null;
	}
	
	public double getMaxFitness()
	{
		return adults.getMaxFitness();
	}
		
	public double getAverageFitness()
	{
		return adults.getAverageFitness();
	}
	
	public void develop()
	{
		List<Individual> removeList = new ArrayList<Individual>();
		
		for (Individual ind : children) 
		{
			Phenotype ptype = developPhenotype(ind.getGenotype());
			if(ptype!=null)
			{
				ind.setPhenotype(ptype);
			}
			else
			{
				removeList.add(ind);
			}
		}
		
		for(Individual ind : removeList)
		{
			children.removeIndividual(ind);
		}
	}
	
	public void evaluate()
	{
		for (Individual ind : children) {
			ind.setFitness(calculateFitness(ind.getPhenotype()));
		}
	}
	
	public void crossover()
	{	
		children.shuffle();
		
		Individual ind1 = null;
		
		for(Individual ind2 : children)
		{
			if(ind1==null)
				ind1 = ind2;
			else
			{
				if(Math.random()<crossoverRate)
				{			
					performCrossover(ind1.getGenotype(),ind2.getGenotype());
					ind1.setParentY(ind2.getParentX());
					ind2.setParentY(ind1.getParentX());
				}
				ind1=null;
			}
		}
	}
	
	public void mutation()
	{
		for (Individual ind : children) {
			performMutation(ind.getGenotype());
		}
		
	}
	
	abstract protected void performCrossover(Genotype genotype, Genotype genotype2);
	
	abstract protected void performMutation(Genotype genotype);
	
	abstract protected Genotype generateRandomGenotype();
	
	abstract protected Phenotype developPhenotype(Genotype gtype);
	
	abstract protected double calculateFitness(Phenotype ptype);
	
	abstract protected void performParentSelection();
	
	abstract protected void performReplacement();
	
	public String toString()
	{
		String str="";
		
		if(children!=null)
		{
			str+="Child population:\n";
			for(Individual ind : children)
			{
				str+=ind.toString()+"\n";
			}
		}
		if(adults!=null)
		{
			str+="Adult population:\n";
			for(Individual ind : adults)
			{
				str+=ind.toString()+"\n";
			}
		}
		
		return str;
	}

	public double getStandardDeviation() {
		return adults.getStandardDeviation();
	}

	public Individual getBestIndividual() {
		return adults.getBestIndividual();
	}

	public abstract double getAdditionalStatistic(String string);
	
	public abstract JPanel getBestIndividualPanel();

	public abstract JPanel getTargetPanel();
}
